//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"
#include "NjTestUnit.h"
#include "NjTimingTest.h"
#include "NjSharedSyncObjectConstants.h"
#include "NjRandUint.h"
#include "NjExampleRandGenerator.h"
#include "NjThreadsSystem.h"
#include "NjHighResolutionTimer.h"

//----------------------------------------------------------------------------
class NjTimingTest::Static
{
public:
	static const NjFloat NJ_PRECISION;

	static void RunSleepTest();
	static void RunTryWaitEventTest();
};
//----------------------------------------------------------------------------
const NjFloat NjTimingTest::Static::NJ_PRECISION(0.1);
//----------------------------------------------------------------------------
void NjTimingTest::Run(void)
{
	Static::RunSleepTest();
	Static::RunTryWaitEventTest();
}
//----------------------------------------------------------------------------
void NjTimingTest::Static::RunSleepTest(void)
{
	NJ_TEST_UNITS_GROUP("NjThreadsSystem/NjEventsSystem");
		NJ_TEST_UNIT("NjThreadsSystem::Sleep, NjEventsSystem::GetTimeMs");
			static const NjUint ITERATIONS=10;

			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand;

			NjUint uIterator(ITERATIONS);
			while (uIterator>0)
			{
				--uIterator;
				NjUint uWaitMs((NjSharedSyncObjectConstants::TIME_GRANULARITY*(kRand()%100+10))/10);
				NjFloat const fTimeMin(NjFloat(uWaitMs)*(1-Static::NJ_PRECISION)/1000);
				NjFloat const fTimeMax(NjFloat(uWaitMs)/(1000*(1-Static::NJ_PRECISION)));
				NjUint uTimeMsStart;
				NjUint uTimeMsEnd;

				NJ_TEST(NjEventsSystem::GetTimeMs(uTimeMsStart));
				NjHighResolutionTimer kTimer;
				kTimer.Start();
				NjThreadsSystem::Sleep(uWaitMs);
				kTimer.Pause();
				NJ_TEST(NjEventsSystem::GetTimeMs(uTimeMsEnd));

				NjFloat const fTimeMs(NjFloat(uTimeMsEnd-uTimeMsStart)/1000);
				NjFloat const fTimeHighRes(kTimer.GetTimeS());
				NJ_TEST(fTimeHighRes>fTimeMin && fTimeHighRes<fTimeMax);
				NJ_TEST(fTimeMs>fTimeMin && fTimeMs<fTimeMax);
			}
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjTimingTest::Static::RunTryWaitEventTest(void)
{
	NJ_TEST_UNITS_GROUP("NjEventsSystem");
		NJ_TEST_UNIT("NjEventsSystem::TryWaitEvent, NjEventsSystem::GetTimeMs");
			NjEventsSystem::Event * const pEvent(NjEventsSystem::CreateEvent(NJ_FALSE));
			NJ_TEST(pEvent!=NJ_NULL);

			static const NjUint ITERATIONS=10;

			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand;

			NjUint uIterator(ITERATIONS);
			while (uIterator>0)
			{
				--uIterator;
				NjUint uWaitMs((NjSharedSyncObjectConstants::TIME_GRANULARITY*(kRand()%100+10))/10);
				NjFloat const fTimeMin(NjFloat(uWaitMs)*(1-Static::NJ_PRECISION)/1000);
				NjFloat const fTimeMax(NjFloat(uWaitMs)/(1000*(1-Static::NJ_PRECISION)));
				NjUint uTimeMsStart;
				NjUint uTimeMsEnd;
				NjEventsSystem::TryResult eResult;

				NJ_TEST(NjEventsSystem::GetTimeMs(uTimeMsStart));
				NjHighResolutionTimer kTimer;
				kTimer.Start();
				eResult=NjEventsSystem::TryWaitEvent(pEvent, uWaitMs);
				kTimer.Pause();
				NJ_TEST(NjEventsSystem::GetTimeMs(uTimeMsEnd));
				NJ_TEST(eResult==NjEventsSystem::TRY_TIMEOUT);

				NjFloat const fTimeMs(NjFloat(uTimeMsEnd-uTimeMsStart)/1000);
				NjFloat const fTimeHighRes(kTimer.GetTimeS());
				NJ_TEST(fTimeHighRes>fTimeMin && fTimeHighRes<fTimeMax);
				NJ_TEST(fTimeMs>fTimeMin && fTimeMs<fTimeMax);
			}

			NJ_TEST(NjEventsSystem::DestroyEvent(pEvent));
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
